var DeliciousBookmarkSystem = Class.create(//
		BookmarkSystem, {
			requestTimeout : 1000 * 60, // 60 seconds
			pollInterval : 1000 * 600, // 10 minute
			scheduleTimerId : 0,
			source : 'delicious',
			query : function(api, args, callbacks) {
				args = args || {};
				var username = this.username;
				delete args.username;
				var password = this.password;
				delete args.password;
				try {
					return new Ajax.Request('https://'
									+ username
									+ ':'
									+ password
									+ '@api.del.icio.us/v1/'
									+ api
									+ (args
											? "?" + Object.toQueryString(args)
											: ""), callbacks);
				} catch (e) {
					console.dir(e.stack || e);
				}
				return {
					abort : function() {
					}
				};
			},

			initialize : function(username, password) {
				this.state = this.synchronizeState;
				this.username = username || localStorage['username'];
				this.password = password || localStorage['password'];
				this.loginStatus = null;
				this.loginJob = this.createJob(createLoginQuery(username,
						password));
				this.fetchJob = this.createJob(createFetchQuery(username,
						password));
				this.updateJob = this.createJob(createUpdateQuery(username,
						password));
			},

			createJob : function(query) {
				var self = this;

				var job = new Job(function() {
					job.request = self
							.query(
									job.key,
									job.args
											|| (typeof(job.getArgs) == 'function' && (job.getArgs
													.apply(self, arguments)))
											|| {}, {
										onSuccess : function() {
											job.done();
											job.request = null;
											job.onSuccess
													.apply(self, arguments);
										},
										onError : function() {
											job.done();
											job.request = null;
											job.onError.apply(self, arguments);
										}
									});
				}, query.delay || 1000, query.timeout || 15000);
				job.postCancel = function() {
					if (job.onTimeout)
						job.onTimeout.apply(self, arguments);
					console.log('default job.postCancel');
					if (job.request && job.request.transport
							&& job.request.transport.abort)
						job.request.transport.abort();
				};
				query.job = job;
				Object.extend(job, query);

				return job;
			},

			notifyLogin : function() {
				this.loginStatus = new Date();
			},
			tryLogin : function() {
				if (this.loginStatus && new Date() - this.loginStatus < 60000) {
					var loginId = randomString();
					window.setTimeout((function() {
								this.loginCallback({
											result : 'success',
											loginId : loginId
										});
							}).bind(this), 10);
					return loginId;
				}
				return this.loginJob.replaceSchedule();
			},
			cancelLogin : function() {
				return this.loginJob.cancel();
			},
			loginCallback : function(args) {
				subPub.publish('delicious/login', args);
			},
			updateCallback : function(args) {
				subPub.publish('delicious/update', args);
			},
			fetchCallback : function(result) {
				this.xmlDoc = result.response
						? result.response.responseXML
						: this.xmlDoc;
				if (this.xmlDoc) {
					var md5s = {};
					for (var it = this.xmlDoc.evaluate('//post', this.xmlDoc), node; node = it
							.iterateNext();) {
						var bookmark = {
							urlMD5 : node.getAttribute('hash'),
							url : node.getAttribute('href'),
							name : node.getAttribute('description'),
							tags : node.getAttribute('tag').split(' '),
							time : node.getAttribute('time'),
							shared : node.getAttribute('shared') != 'no'
						};
						md5s[node.getAttribute('hash')] = true;
						var t = new Date();
						t.setISO8601(bookmark.time);
						bookmark.time = t;
						bookmarkStorage.updateBookmark(bookmark, 'delicious');
					}
					{
						var it = this.xmlDoc.evaluate('//post', this.xmlDoc);
					}
					if (this.lastUpdated > bookmarkStorage.lastUpdated) {
						var toDelete = [];
						for (var md5 in bookmarkStorage.getCache()) {
							var bookmark = bookmarkStorage.getCache()[md5];
							if (bookmark
									&& bookmark.urlMD5
									&& !md5s[bookmark.urlMD5]
									&& (!bookmark.time || bookmark.time < this.lastUpdated)) {
								toDelete.push(bookmark.urlMD5);
							}
						}
						toDelete.each(function(md5) {
									bookmarkStorage.deleteBookmark(md5,
											'delicious');
								});
					}
				}
				subPub.publish('delicious/fetch', result);
			},
			scheduleLoading : function() {
				if (this.updatedTime && this.lastFetch > this.updatedTime) {
					var id = randomString();
					window.setTimeout((function() {
								this.fetchCallback({
											result : 'success',
											loginId : id
										});
							}).bind(this), 0);
					return id;
				}
				return this.fetchJob.replaceSchedule();
			}
		});